package com.simple.gateway.pay.filter;


import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.simple.gateway.pay.config.GatewayConfig;
import com.simple.gateway.pay.strategy.BodySignStrategy;
import com.simple.gateway.pay.utils.SignUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.http.ServletInputStreamWrapper;
import com.simple.common.entity.ResData;
import com.simple.common.entity.po.MerchantEntity;
import com.simple.ds.service.MerchantService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.Objects;


/**
 * 前置拦截器
 * 从http 头信息中获取  serviceId 和 signInfo，以及 ts
 */
@Slf4j
@Component
public class RequestFilter extends ZuulFilter {


    public static final ThreadLocal<Long> TIME = new ThreadLocal<>();
    public static final ThreadLocal<String> MID = new ThreadLocal<>();
    public static final ThreadLocal<String> APIKEY = new ThreadLocal<>();

    @Autowired
    GatewayConfig config;
    @Autowired
    MerchantService merchantService;
    @Autowired
    Gson gson;

    ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        HttpServletRequest request = RequestContext.getCurrentContext().getRequest();
        String requestURI = request.getRequestURI();
        if (requestURI.startsWith("/api/receiver")) {
            return false;
        }
        return true;
    }

    @Override
    public Object run() {

        long startTime = System.currentTimeMillis();
        log.info("网关处理==>>{},开始时间为：==>>{}", "开始", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        TIME.set(startTime);
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        String uri = request.getRequestURI();

        String mid = request.getHeader("mid");
        String ts = request.getHeader("ts");
        String signature = request.getHeader("signature");
        String version = request.getHeader("version");
        MID.set(mid);

        if (StringUtils.isBlank(mid) || StringUtils.isBlank(ts) || StringUtils.isBlank(signature) || StringUtils.isBlank(version)) {
            log.info("商户号【{}】请求处理==>>{},原因为==>>{}", MID.get(), "异常结束", "Post body is not chunkedRequestBody");
            reponseError(ctx, ResData.error("header parmas is missing"));
            return null;
        }

        try {
            MerchantEntity merchantEntity = merchantService.getOne(Wrappers.<MerchantEntity>lambdaQuery().eq(MerchantEntity::getMerchantCode, mid));
            if (Objects.isNull(merchantEntity) || !merchantEntity.getEnabled()) {
                log.info("商户号【{}】请求处理==>>{},原因为==>>{}", MID.get(), "异常结束", "merchant is not exist or merchant is closed");
                reponseError(ctx, ResData.error("merchant is not exist or merchant is closed"));
                return null;
            }
            APIKEY.set(merchantEntity.getApiKey());

            if (config.getIsTest()) {
                log.info("商户号【{}】请求处理==>>{}", MID.get(), "测试环境主动忽略验证");
                return null;
            }

            if (isFilterUrl(uri)) {
                log.info("商户号【{}】请求处理==>>{},原因==>>路径【{}】为忽略验证路径", MID.get(), "结束", uri);
                return null;
            }

            //IP检查
            String remoteIp = getRemoteClientIp(ctx.getRequest());
            if (StringUtils.isNotBlank(merchantEntity.getIpAddr())) {
                if (!merchantEntity.getIpAddr().contains(remoteIp)) {
                    log.info("商户号【{}】请求处理==>>{},原因为==>>{}", MID.get(), "异常结束", "Illegal IP request");
                    reponseError(ctx, ResData.error("Illegal IP request"));
                    return null;
                }
            }

            if (Objects.equals("GET", request.getMethod())) {
                log.info("商户号【{}】请求处理==>>{},原因为==>>{}", MID.get(), "异常结束", "Request Method Is Not Support");
                reponseError(ctx, ResData.error("Request Method Is Not Support"));
                return null;
            }

            if (ctx.isChunkedRequestBody()) {
                log.info("商户号【{}】请求处理==>>{},原因为==>>{}", MID.get(), "异常结束", "Post body is not chunkedRequestBody");
                reponseError(ctx, ResData.error("The request body must support chunked"));
                return null;
            }

            String bodyStr = "";
            try {
                ServletInputStream inp = ctx.getRequest().getInputStream();
                bodyStr = IOUtils.toString(inp, "UTF-8");
                log.info("商户号【{}】请求处理==>>{},获取结果==>>{},body参数为==>>{}", MID.get(), "body参数获取中", "成功", StringUtils.isEmpty(bodyStr) ? null : bodyStr);
            } catch (IOException e) {
                log.info("商户号【{}】请求处理==>>{},原因为==>>{}", MID.get(), "异常结束", "Body Params Exception");
                reponseError(ctx, ResData.error("Body Params Exception"));
                return null;
            }

            if (StringUtils.isBlank(bodyStr)) {
                log.info("商户号【{}】请求处理==>>{},原因为==>>{}", MID.get(), "异常结束", "Body Params Exception");
                reponseError(ctx, ResData.error("Body Params Exception"));
                return null;
            }

            Map<String, Object> signMap;
            try {
                signMap = new BodySignStrategy().requestSign(ctx.getRequest());
            } catch (Exception e) {
                log.info("商户号【{}】请求处理==>>{},原因为==>>{}", MID.get(), "异常结束", "Body Params Exception");
                reponseError(ctx, ResData.error("Body Params Exception"));
                return null;
            }

            if (!SignUtil.verifySign(signMap, APIKEY.get(), signature, MID.get(), "请求处理")) {
                reponseError(ctx, ResData.error("Signature verification failed"));
                return false;
            }
            String finalBodyStr = bodyStr;
            ctx.setRequest(new HttpServletRequestWrapper(request) {

                @Override
                public ServletInputStream getInputStream() throws IOException {
                    Map<String, Object> value = objectMapper.readValue(finalBodyStr, new TypeReference<Map<String, Object>>() {});
                    value.put("appId", merchantEntity.getMerchantCode());
                    value.put("version", version);
                    return new ServletInputStreamWrapper(objectMapper.writeValueAsBytes(value));
                }
            });
            ctx.getResponse().setContentType("application/json;charset=utf-8");
            log.info("商户号【{}】请求处理==>>{},", MID.get(), "成功通过验证");
            return true;
        } catch (RuntimeException e) {
            log.info("商户号【{}】请求处理==>>{},原因为==>>{}", MID.get(), "异常结束", e.getMessage());
            reponseError(ctx, ResData.error(e.getMessage()));
            return false;
        }
    }

    /**
     * 判断是否过滤路径
     *
     * @param uri
     * @return
     */
    private boolean isFilterUrl(String uri) {
        if (uri.contains("callback")
                || uri.contains("notify")
        ) {
            return true;
        }
        return false;
    }

    /**
     * 网关响应
     *
     * @param ctx
     * @param back
     * @return
     */
    private void reponseError(RequestContext ctx, ResData back) {
        ctx.setSendZuulResponse(false);
        ctx.setResponseStatusCode(200);
        ctx.setResponseBody(gson.toJson(back));
        ctx.getResponse().setContentType("application/json;charset=UTF-8");
    }

    /**
     * 获取ip
     *
     * @param request
     * @return
     */
    public static String getRemoteClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (!StringUtils.isEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
            //多次反向代理后会有多个ip值，第一个ip才是真实ip
            int index = ip.indexOf(",");
            if (index != -1) {
                return ip.substring(0, index);
            } else {
                return ip;
            }
        }
        ip = request.getHeader("X-Real-IP");
        if (!StringUtils.isEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
            return ip;
        }
        return request.getRemoteAddr();
    }
}
